#include "STMSPIDMA.h"

void SPIDMAClass::begin(uint32_t Priority) {
  beginTransaction(SPISettings(36*1000*1000, MSBFIRST, SPI_MODE0));
  _hwspi = getHandle()->Instance;//SPI2;
  _Priority = Priority;
  if (_hwspi == SPI1) {
    _dma = DMA1;
    _rxchannel = LL_DMA_CHANNEL_2;
    _txchannel = LL_DMA_CHANNEL_3;
  }
  else if (_hwspi == SPI2) {
    _dma = DMA1;
    _rxchannel = LL_DMA_CHANNEL_4;
    _txchannel = LL_DMA_CHANNEL_5;
  }
  else {
#ifdef DMA2
    if (_hwspi == SPI3) {
      _dma = DMA2;
      _rxchannel = LL_DMA_CHANNEL_1;
      _txchannel = LL_DMA_CHANNEL_2;
    }
#else
    _dma = NULL;
#endif
  }
  DMAinit();
}

void SPIDMAClass::end() {
  DisableDMA();
  LL_DMA_DeInit(_dma, _txchannel);
  LL_DMA_DeInit(_dma, _rxchannel);
  DisableClock();
  SPIClass::end();
}

void SPIDMAClass::DMAinit() {
  DisableDMA();                                                    //禁用DMA，初始化前需禁用DMA
  LL_DMA_DeInit(_dma, _txchannel);                                 //dma通道寄存器重置
  LL_DMA_DeInit(_dma, _rxchannel);                                 //dma通道寄存器重置
  EnableClock();                                                   //启用时钟
  LL_DMA_InitTypeDef DMAConfig;                                    //定义dma通道参数变量
  LL_DMA_StructInit(&DMAConfig);                                   //初始化dma通道参数变量
  DMAConfig.PeriphOrM2MSrcAddress = LL_SPI_DMA_GetRegAddr(_hwspi); //设定外设地址
  DMAConfig.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;         //传输方向
  DMAConfig.Mode = LL_DMA_MODE_NORMAL;                             //DMA传输方式 正常模式/循环模式
  DMAConfig.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;     //外围增量模式禁用或启用，外围设备不要开启增量。
  DMAConfig.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;       //内存增量模式禁用或启用，内存需要开启增量
  DMAConfig.PeriphOrM2MSrcDataSize =                               //外围数据宽度
    LL_SPI_GetDataWidth(_hwspi) == LL_SPI_DATAWIDTH_8BIT
    ? LL_DMA_PDATAALIGN_BYTE
    : LL_DMA_PDATAALIGN_HALFWORD;
  DMAConfig.MemoryOrM2MDstDataSize =                               //内存数据宽度
    LL_SPI_GetDataWidth(_hwspi) == LL_SPI_DATAWIDTH_8BIT
    ? LL_DMA_PDATAALIGN_BYTE
    : LL_DMA_PDATAALIGN_HALFWORD;
  DMAConfig.Priority = _Priority;                                  //优先级
  LL_DMA_Init(_dma, _txchannel, &DMAConfig);                       //初始化DMA通道
  DMAConfig.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY;         //传输方向
  LL_DMA_Init(_dma, _rxchannel, &DMAConfig);                       //初始化DMA通道
}



byte SPIDMAClass::DMAtransfer(uint8_t _data) {
  DMAtransfer(&_data, sizeof(uint8_t));
  return _data;
}

uint16_t SPIDMAClass::DMAtransfer16(uint16_t _data) {
  DMAtransfer(&_data, sizeof(uint16_t));
  return _data;
}

void SPIDMAClass::DMAtransfer(void* _buf, size_t _count) {
  DisableDMA(); //禁用DMA
  LL_DMA_SetMemoryAddress(_dma, _txchannel, (uint32_t)_buf);//设置要传输的数据地址
  LL_DMA_SetDataLength(_dma, _txchannel, _count);//设置要传输的数据数量
  LL_DMA_SetMemoryAddress(_dma, _rxchannel, (uint32_t)_buf);//设置要传输的数据地址
  LL_DMA_SetDataLength(_dma, _rxchannel, _count);//设置要传输的数据数量
  LL_SPI_EnableDMAReq_RX(_hwspi);//启动spi DMA
  LL_DMA_EnableChannel(_dma, _rxchannel);//启动DMA
  LL_SPI_EnableDMAReq_TX(_hwspi);//启动spi DMA
  LL_DMA_EnableChannel(_dma, _txchannel);//启动DMA
  while (!IsActiveFlag_TC(_dma, _txchannel))//检查是否传输完成
    ;
  while (!IsActiveFlag_TC(_dma, _rxchannel))//检查是否传输完成
    ;
  ClearFlag_TC(_dma, _txchannel);//清除完成标志
  ClearFlag_TC(_dma, _rxchannel);//清除完成标志

  while (LL_SPI_IsActiveFlag_BSY(_hwspi))//检查spi是否完成
    ;
  DisableDMA();
  LL_SPI_DisableDMAReq_TX(_hwspi);
  LL_SPI_DisableDMAReq_RX(_hwspi);
}





